<template>
  <div class="tutorial-page">
    <div class="tutorial-header">
      <h1>Vue Router</h1>
      <p>学习路由配置、导航守卫和路由传参</p>
    </div>

    <div class="tutorial-content">
      <div class="section">
        <h2>Vue Router 简介</h2>
        <p>Vue Router 是 Vue.js 官方的路由管理器。它与 Vue.js 的核心深度集成，让构建单页面应用变得易如反掌。</p>
        
        <h3>功能特性</h3>
        <ul>
          <li>嵌套的路由/视图表</li>
          <li>模块化的、基于组件的路由配置</li>
          <li>路由参数、查询、通配符</li>
          <li>基于 Vue.js 过渡系统的视图过渡效果</li>
          <li>细粒度的导航控制</li>
          <li>带有自动激活的 CSS class 的链接</li>
          <li>HTML5 历史模式或 hash 模式</li>
          <li>可扩展的、自定义的导航守卫</li>
        </ul>
      </div>

      <div class="section">
        <h2>基础路由配置</h2>
        
        <h3>安装和基本配置</h3>
        <pre><code class="language-bash"># 安装 Vue Router
npm install vue-router@4</code></pre>
        
        <pre><code class="language-javascript">// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const routes = [
  {
    path: '/',
    name: 'home',
    component: HomeView
  },
  {
    path: '/about',
    name: 'about',
    component: () => import('../views/AboutView.vue')
  }
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})

export default router</code></pre>
        
        <pre><code class="language-javascript">// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

createApp(App).use(router).mount('#app')</code></pre>
        
        <pre><code class="language-html">&lt;!-- App.vue --&gt;
&lt;template&gt;
  &lt;div id="app"&gt;
    &lt;router-link to="/"&gt;Home&lt;/router-link&gt;
    &lt;router-link to="/about"&gt;About&lt;/router-link&gt;
    &lt;router-view&gt;&lt;/router-view&gt;
  &lt;/div&gt;
&lt;/template&gt;</code></pre>
      </div>

      <div class="section">
        <h2>动态路由匹配</h2>
        <p>我们经常需要把某种模式匹配到的所有路由，全都映射到同个组件。</p>
        
        <pre><code class="language-javascript">const routes = [
  // 动态路径参数 以冒号开头
  { path: '/user/:id', component: User }
]</code></pre>
        
        <pre><code class="language-html">&lt;!-- User.vue --&gt;
&lt;template&gt;
  &lt;div&gt;
    &lt;h2&gt;User {{ $route.params.id }}&lt;/h2&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { useRoute } from 'vue-router'

const route = useRoute()
console.log(route.params.id)
&lt;/script&gt;</code></pre>
        
        <h3>响应路由参数的变化</h3>
        <pre><code class="language-javascript">import { watch } from 'vue'
import { useRoute } from 'vue-router'

export default {
  setup() {
    const route = useRoute()

    watch(
      () => route.params.id,
      (newId, oldId) => {
        // 对路由变化做出响应...
      }
    )
  }
}</code></pre>
        
        <h3>捕获所有路由或 404 Not found 路由</h3>
        <pre><code class="language-javascript">const routes = [
  // 会匹配所有路径
  { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
  // 会匹配以 `/user-` 开头的任意路径
  { path: '/user-:afterUser(.*)', component: UserCustom }
]</code></pre>
      </div>

      <div class="section">
        <h2>嵌套路由</h2>
        <p>一些应用程序的 UI 由多层嵌套的组件组成。在这种情况下，URL 的片段通常对应于特定的嵌套组件结构。</p>
        
        <pre><code class="language-javascript">const routes = [
  {
    path: '/user/:id',
    component: User,
    children: [
      // 当 /user/:id 匹配成功，
      // UserHome 会被渲染在 User 的 &lt;router-view&gt; 中
      { path: '', component: UserHome },
      
      // 当 /user/:id/profile 匹配成功，
      // UserProfile 会被渲染在 User 的 &lt;router-view&gt; 中
      { path: 'profile', component: UserProfile },
      
      // 当 /user/:id/posts 匹配成功
      // UserPosts 会被渲染在 User 的 &lt;router-view&gt; 中
      { path: 'posts', component: UserPosts }
    ]
  }
]</code></pre>
        
        <pre><code class="language-html">&lt;!-- User.vue --&gt;
&lt;template&gt;
  &lt;div class="user"&gt;
    &lt;h2&gt;User {{ $route.params.id }}&lt;/h2&gt;
    &lt;router-link to="/user/123/profile"&gt;Profile&lt;/router-link&gt;
    &lt;router-link to="/user/123/posts"&gt;Posts&lt;/router-link&gt;
    &lt;router-view&gt;&lt;/router-view&gt;
  &lt;/div&gt;
&lt;/template&gt;</code></pre>
      </div>

      <div class="section">
        <h2>编程式导航</h2>
        
        <h3>在 Composition API 中使用</h3>
        <pre><code class="language-javascript">import { useRouter, useRoute } from 'vue-router'

export default {
  setup() {
    const router = useRouter()
    const route = useRoute()

    function pushToHome() {
      // 字符串路径
      router.push('/home')
      
      // 对象
      router.push({ path: '/home' })
      
      // 命名的路由
      router.push({ name: 'user', params: { userId: '123' } })
      
      // 带查询参数，结果是 /register?plan=private
      router.push({ path: '/register', query: { plan: 'private' } })
      
      // 带 hash，结果是 /about#team
      router.push({ path: '/about', hash: '#team' })
    }
    
    return {
      pushToHome
    }
  }
}</code></pre>
        
        <h3>替换当前位置</h3>
        <pre><code class="language-javascript">// 替换当前位置，不会向 history 添加新记录
router.replace({ path: '/home' })

// 相当于
router.push({ path: '/home' }, undefined, { replace: true })</code></pre>
        
        <h3>前进/后退</h3>
        <pre><code class="language-javascript">// 向前移动一条记录，与 router.forward() 相同
router.go(1)

// 返回一条记录，与 router.back() 相同
router.go(-1)

// 前进 3 条记录
router.go(3)

// 如果没有那么多记录，静默失败
router.go(-100)
router.go(100)</code></pre>
      </div>

      <div class="section">
        <h2>命名路由</h2>
        <p>除了 path 之外，我们还可以为任何路由提供 name。这有以下优点：</p>
        <ul>
          <li>没有硬编码的 URL</li>
          <li>params 的自动编码/解码</li>
          <li>防止你在 url 中出现打字错误</li>
          <li>绕过路径排序（如显示一个）</li>
        </ul>
        
        <pre><code class="language-javascript">const routes = [
  {
    path: '/user/:userId/books/:bookId',
    name: 'book',
    component: Book
  }
]</code></pre>
        
        <pre><code class="language-html">&lt;!-- 使用 router-link 组件 --&gt;
&lt;router-link :to="{ name: 'book', params: { userId: 123, bookId: 456 }}"&gt;
  Book
&lt;/router-link&gt;</code></pre>
        
        <pre><code class="language-javascript">// 使用 router.push()
router.push({ name: 'book', params: { userId: 123, bookId: 456 } })</code></pre>
      </div>

      <div class="section">
        <h2>命名视图</h2>
        <p>有时候想同时 (同级) 展示多个视图，而不是嵌套展示。</p>
        
        <pre><code class="language-html">&lt;router-view class="view left"&gt;&lt;/router-view&gt;
&lt;router-view class="view right" name="a"&gt;&lt;/router-view&gt;
&lt;router-view class="view bottom" name="b"&gt;&lt;/router-view&gt;</code></pre>
        
        <pre><code class="language-javascript">const routes = [
  {
    path: '/',
    components: {
      default: Home,
      a: Sidebar,
      b: Bottom
    }
  }
]</code></pre>
      </div>

      <div class="section">
        <h2>路由传参</h2>
        
        <h3>路由参数</h3>
        <pre><code class="language-javascript">const routes = [
  {
    path: '/user/:id',
    component: User
  }
]</code></pre>
        
        <h3>查询参数</h3>
        <pre><code class="language-javascript">// /user?name=john&age=25
router.push({ path: '/user', query: { name: 'john', age: 25 } })</code></pre>
        
        <h3>Props 传参</h3>
        <pre><code class="language-javascript">const routes = [
  {
    path: '/user/:id',
    component: User,
    props: true // 会将 route.params 设置为组件属性
  },
  {
    path: '/promotion/from-newsletter',
    component: Promotion,
    props: { newsletterPopup: false } // 对象模式
  },
  {
    path: '/search',
    component: SearchUser,
    props: (route) => ({ query: route.query.q }) // 函数模式
  }
]</code></pre>
      </div>

      <div class="section">
        <h2>导航守卫</h2>
        <p>正如其名，vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。</p>
        
        <h3>全局前置守卫</h3>
        <pre><code class="language-javascript">const router = createRouter({...})

router.beforeEach((to, from, next) => {
  // to: 即将要进入的目标路由对象
  // from: 当前导航正要离开的路由
  // next: 一定要调用该方法来 resolve 这个钩子
  
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  else next()
})</code></pre>
        
        <h3>全局后置钩子</h3>
        <pre><code class="language-javascript">router.afterEach((to, from) => {
  // 不接受 next 函数也不会改变导航本身
  document.title = to.meta.title || 'Default Title'
})</code></pre>
        
        <h3>路由独享的守卫</h3>
        <pre><code class="language-javascript">const routes = [
  {
    path: '/users/:id',
    component: UserDetails,
    beforeEnter: (to, from, next) => {
      // 对特定路由进行验证
      if (to.params.id === '1') {
        next({ name: 'Home' })
      } else {
        next()
      }
    }
  }
]</code></pre>
        
        <h3>组件内的守卫</h3>
        <pre><code class="language-javascript">export default {
  // 在渲染该组件的对应路由被验证前调用
  beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被验证前调用
    // 不能获取组件实例 `this`！
    next(vm => {
      // 通过 `vm` 访问组件实例
    })
  },
  
  // 在当前路由改变，但是该组件被复用时调用
  beforeRouteUpdate(to, from, next) {
    // 举例来说，对于带有动态参数的路径 `/users/:id`，在 `/users/1` 和 `/users/2` 之间跳转的时候，
    // 由于会渲染同样的 Foo 组件，因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
    next()
  },
  
  // 在导航离开渲染该组件的路由时调用
  beforeRouteLeave(to, from, next) {
    // 可以访问组件实例 `this`
    const answer = window.confirm('Do you really want to leave?')
    if (answer) {
      next()
    } else {
      next(false)
    }
  }
}</code></pre>
      </div>

      <div class="section">
        <h2>路由元信息</h2>
        <p>定义路由的时候可以配置 meta 字段：</p>
        
        <pre><code class="language-javascript">const routes = [
  {
    path: '/admin',
    component: Admin,
    meta: { requiresAuth: true }
  }
]</code></pre>
        
        <pre><code class="language-javascript">router.beforeEach((to, from, next) => {
  if (to.meta.requiresAuth && !isAuthenticated) {
    next('/login')
  } else {
    next()
  }
})</code></pre>
      </div>

      <div class="section">
        <h2>路由过渡动效</h2>
        <pre><code class="language-html">&lt;router-view v-slot="{ Component }"&gt;
  &lt;transition name="slide" mode="out-in"&gt;
    &lt;component :is="Component" /&gt;
  &lt;/transition&gt;
&lt;/router-view&gt;</code></pre>
        
        <pre><code class="language-css">.slide-enter-active,
.slide-leave-active {
  transition: opacity 0.5s ease;
}

.slide-enter-from,
.slide-leave-to {
  opacity: 0;
}</code></pre>
      </div>

      <div class="section">
        <h2>滚动行为</h2>
        <pre><code class="language-javascript">const router = createRouter({
  history: createWebHistory(),
  routes: [...],
  scrollBehavior(to, from, savedPosition) {
    // return 期望滚动到哪个的位置
    if (savedPosition) {
      return savedPosition
    } else {
      return { top: 0 }
    }
  }
})</code></pre>
      </div>
    </div>
  </div>
</template>

<script setup>
// 页面逻辑可以在这里添加
</script>

<style scoped>
@import '@/assets/tutorial-styles.css';
</style>